﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Lidgren.Network;

namespace Engine.Networking.Server
{
    public abstract class GameServer
    {
        public double UpdateRate = 1.0 / 30.0;

        protected NetServer mServer;
        double nextSendUpdates;
        volatile bool running;
        public NetPeerStatus Status { get { return mServer.Status; } }
        public int ConnectionCount { get { return mServer.ConnectionsCount; } }

        public GameServer(int _port, string _appID)
        {
            NetPeerConfiguration config = new NetPeerConfiguration(_appID);
            config.EnableMessageType(NetIncomingMessageType.ConnectionApproval);
            config.Port = _port;
            mServer = new NetServer(config);
            
            running = false;
            nextSendUpdates = NetTime.Now;
        }

        public void Run()
        {
            try
            {
                mServer.Start();
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                mServer.Shutdown("Failure");
                return;
            }
            running = true;
			while (running)
			{
				NetIncomingMessage msg;
				while ((msg = mServer.ReadMessage()) != null)
				{
					switch (msg.MessageType)
					{
						case NetIncomingMessageType.DiscoveryRequest:
							// Server received a discovery request from a client; send a discovery response (with no extra data attached)
							mServer.SendDiscoveryResponse(null, msg.SenderEndpoint);
							break;
                        case NetIncomingMessageType.ConnectionApproval:
                            ConnectionApproval(msg);
                            break;
						case NetIncomingMessageType.VerboseDebugMessage:
						case NetIncomingMessageType.DebugMessage:
						case NetIncomingMessageType.WarningMessage:
						case NetIncomingMessageType.ErrorMessage:
							// Just print diagnostic messages to console
							Console.WriteLine(msg.ReadString());
							break;
						case NetIncomingMessageType.StatusChanged:
							NetConnectionStatus status = (NetConnectionStatus)msg.ReadByte();
							if (status == NetConnectionStatus.Connected)
							{
								// A new player just connected!
								Console.WriteLine(NetUtility.ToHexString(msg.SenderConnection.RemoteUniqueIdentifier) + " connected!");
                                ClientConnected(msg);
                            }
                            else if (status == NetConnectionStatus.Disconnected)
                            {
                                Console.WriteLine(NetUtility.ToHexString(msg.SenderConnection.RemoteUniqueIdentifier) + " disconneted!");
                                ClientDisconnected(msg);
                            }

							break;
						case NetIncomingMessageType.Data:
                            DataRecieved(msg);
							break;
					}

					double now = NetTime.Now;
					if (now > nextSendUpdates)
					{
                        Update();
						// schedule next update
						nextSendUpdates += UpdateRate;
					}
				}
				// sleep to allow other processes to run smoothly
				Thread.Sleep(1);
			}
            ServerShutDown();
			mServer.Shutdown("app exiting");
        }

        public void Stop()
        {
            running = false;
            ServerShutDown();
        }

        public virtual void ServerShutDown()
        {

        }

        public virtual void ConnectionApproval(NetIncomingMessage _msg)
        {

        }

        public virtual void ClientConnected(NetIncomingMessage _msg)
        {

        }

        public virtual void ClientDisconnected(NetIncomingMessage _msg)
        {

        }

        public virtual void DataRecieved(NetIncomingMessage _msg)
        {

        }

        public virtual void Update()
        {

        }

    }
}
